package cn.fj.xiaowei.main.common.generator;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * 代码生成器，
 * 基于 MyBatis-Plus 中的快速代码生成器 FastAutoGenerator 实现.
 * <p></p>
 * 其中方法调用应当遵循的顺序：create() [-> setXxx()] -> initGenerator() [-> customGenerator()] -> run()
 */
@AllArgsConstructor
@Data
@Accessors(chain = true) // 让生成 setter 方法返回 this，以实现链式调用
public class CodeGeneratorBaseMP {

    // 数据库连接信息
    private String driver = "com.mysql.cj.jdbc.Driver";
    private String url;
    private String username;
    private String password;

    // 作者
    private String author = "xiaowei";
    // 注释日期格式
    private String commentDatePattern = "yyyy-MM-dd HH:mm:ss";
    // 是否开启 kotlin 模式
    private boolean kotlin = false;
    // 是否开启 swagger 模式
    private boolean swagger = false;
    // 代码生成后文件的存放目录
    private String outputDir = System.getProperty("user.dir") + "/src/main/java";
    // 代码生成后是否在资源管理器中打开代码文件所在目录
    private boolean openOutputDir = false;

    // 父包名
    private String parentPackage;
    // 父包下的模块名
    private String subModuleName = "";
    // 实体类包名
    private String entityPackage = "po";
    // Mapper 包名
    private String mapperPackage = "mapper";
    // Mapper XML 包名
    private String mapperXmlPackage = "mapper";
    // Service 包名
    private String servicePackage = "service";
    // Service Impl 包名
    private String serviceImplPackage = "service.impl";
    // Controller 包名
    private String controllerPackage = "controller";

    // 是否开启实体类覆盖已生成文件
    private boolean entityOverride = false;
    // 是否启用 Lombok
    private boolean lombok = true;
    // 数据库表映射到实体的命名策略
    private NamingStrategy naming = NamingStrategy.underline_to_camel;
    // 是否开启生成实体时生成字段注解
    private boolean tableFieldAnnotation = true;
    // 主键类型策略
    private IdType idType = IdType.ASSIGN_UUID;
    // 数据库中逻辑删除字段名
    private String logicDeleteColumnName = "is_deleted";
    // 实体类文件名称格式
    private String entityFileNamePattern = "%sPo";

    // 是否开启 Mapper 覆盖已生成文件
    private boolean mapperOverride = false;
    // 是否开启 @Mapper 注解
    private boolean mapperAnnotation = true;
    // mapper 文件名称格式
    private String mapperFileNamePattern = "%sMapper";
    // mapper xml 文件名称格式
    private String mapperXmlFileNamePattern = "%sMapper";

    // 是否开启 Service 覆盖已生成文件
    private boolean serviceOverride = false;
    // service 文件名称格式
    private String serviceFileNamePattern = "%sService";
    // service impl 文件名称格式
    private String serviceImplFileNamePattern = "%sServiceImpl";

    // 是否开启 Controller 覆盖已生成文件
    private boolean controllerOverride = false;
    // 是否开启生成 @RestController 控制器
    private boolean restController = true;
    // controller 文件名称格式
    private String controllerFileNamePattern = "%sController";

    // 快速代码生成器
    private FastAutoGenerator generator;

    private CodeGeneratorBaseMP() {
    }

    /**
     * 创建快速生成代码的代码生成器
     *
     * @param url           连接数据库的 URL
     * @param username      数据库用户名
     * @param password      数据库用户密码
     * @param parentPackage 生成的代码的父包名
     * @return 快速生成代码的代码生成器
     */
    public static CodeGeneratorBaseMP create(String url, String username, String password, String parentPackage) {
        CodeGeneratorBaseMP generator = new CodeGeneratorBaseMP();
        generator.url = url;
        generator.username = username;
        generator.password = password;
        generator.parentPackage = parentPackage;
        return generator;
    }

    /**
     * 根据配置初始化快速代码生成器
     */
    public CodeGeneratorBaseMP initGenerator() {
        this.generator = FastAutoGenerator
                // 创建代码生成器
                .create(url, username, password)
                // 代码生成器全局配置
                .globalConfig(
                        builder -> {
                            builder
                                    // 生成代码的输出目录
                                    .outputDir(outputDir)
                                    // 作者
                                    .author(author)
                                    // 注释日期格式
                                    .commentDate(commentDatePattern)
                                    // 生成的实体类中的日期类型，数据库中 datetime 对应 Java 中的 Date
                                    .dateType(DateType.ONLY_DATE);
                            // 开启 kotlin 模式
                            if (kotlin) builder.enableKotlin();
                            // 开启 swagger 模式
                            if (swagger) builder.enableSwagger();
                            // 代码生成后不在资源管理器中打开代码文件所在目录
                            if (!openOutputDir) builder.disableOpenDir();
                        }
                )
                // 生成代码的包配置
                .packageConfig(
                        builder -> {
                            builder
                                    // 父包名
                                    .parent(parentPackage)
                                    // 父包下的模块名
                                    .moduleName(subModuleName)
                                    // 实体类包名
                                    .entity(entityPackage)
                                    // Mapper 包名
                                    .mapper(mapperPackage)
                                    // Mapper XML 包名
                                    .xml(mapperXmlPackage)
                                    // Service 包名
                                    .service(servicePackage)
                                    // Service Impl 包名
                                    .serviceImpl(serviceImplPackage)
                                    // Controller 包名
                                    .controller(controllerPackage);
                        }
                )
                // 策略配置
                .strategyConfig(
                        builder -> {
                            // 实体类策略配置
                            builder.entityBuilder()
                                    // 数据库表映射到实体的命名策略
                                    .naming(naming)
                                    // 主键类型策略
                                    .idType(idType)
                                    // 数据库中逻辑删除字段名
                                    .logicDeleteColumnName(logicDeleteColumnName)
                                    // 实体类文件名称格式
                                    .formatFileName(entityFileNamePattern);
                            // 开启实体类覆盖已生成文件
                            if (entityOverride) builder.entityBuilder().enableFileOverride();
                            // 启用 Lombok
                            if (lombok) builder.entityBuilder().enableLombok();
                            // 开启生成实体时生成字段注解
                            if (tableFieldAnnotation) builder.entityBuilder().enableTableFieldAnnotation();

                            // Mapper 策略配置
                            builder.mapperBuilder()
                                    // mapper 文件名称格式
                                    .formatMapperFileName(mapperFileNamePattern)
                                    // mapper xml 文件名称格式
                                    .formatXmlFileName(mapperXmlFileNamePattern);
                            // 开启 Mapper 覆盖已生成文件
                            if (mapperOverride) builder.mapperBuilder().enableFileOverride();
                            // 开启 @Mapper 注解
                            if (mapperAnnotation) builder.mapperBuilder().enableMapperAnnotation();

                            // Service 策略配置
                            builder.serviceBuilder()
                                    // service 文件名称格式
                                    .formatServiceFileName(serviceFileNamePattern)
                                    // service impl 文件名称格式
                                    .formatServiceImplFileName(serviceImplFileNamePattern);
                            // 开启 Service 覆盖已生成文件
                            if (serviceOverride) builder.serviceBuilder().enableFileOverride();

                            // Controller 策略配置
                            builder.controllerBuilder()
                                    .formatFileName(controllerFileNamePattern);
                            // 开启 Controller 覆盖已生成文件
                            if (controllerOverride) builder.controllerBuilder().enableFileOverride();
                            // 开启生成 @RestController 控制器
                            if (restController) builder.controllerBuilder().enableRestStyle();
                        }
                );
        // 返回 CodeFastGenerator 实例对象，以便链式调用
        return this;
    }

    /**
     * 函数式接口，用于自定义代码生成器
     */
    @FunctionalInterface
    public interface CustomGenerator<T> {

        /**
         * 自定义代码生成器
         *
         * @param generator 代码生成器
         */
        void custom(T generator);
    }

    /**
     * 自定义扩展代码生成器
     *
     * @param customGenerator 函数式接口，用于实现自定义扩展代码生成器
     */
    public CodeGeneratorBaseMP customGenerator(CustomGenerator<FastAutoGenerator> customGenerator) {
        customGenerator.custom(generator);
        return this;
    }

    /**
     * 执行代码生成器，根据配置生成代码
     */
    public void run() {
        // 执行代码生成器生成代码
        generator.execute();
    }

}
